//
// potion.h
//
// (c) 2008 why the lucky stiff, the freelance professor
//
#ifndef POTION_H
#define POTION_H

#define POTION_VERSION  "0.0"
#define POTION_MINOR    0
#define POTION_MAJOR    0
#define POTION_SIG      "p\07\10n"
#define POTION_SIG_LEN  (sizeof POTION_SIG) - 1
#define POTION_VMID     0x79

#define PN_EXT_SRC      ".pn"
#define PN_EXT_BYTECODE ".pnb"
#define PN_EXT_DYLIB    ".so"

#include <limits.h>
#include <string.h>
#include "config.h"

//
// types
//
typedef unsigned long _PN;
typedef unsigned int PN_SIZE, PNType, PNUniq;
typedef struct Potion_State Potion;
typedef volatile _PN PN;
typedef unsigned char PN_FLAGS;

struct PNObject;
struct PNFwd;
struct PNData;
struct PNString;
struct PNBytes;
struct PNDecimal;
struct PNClosure;
struct PNProto;
struct PNTuple;
struct PNWeakRef;
struct PNError;
struct PNCont;
struct PNMemory;
struct PNVtable;

// 只支持 64 或 32 位
/* patch for issue #1 supplied by joeatwork */
#ifndef __WORDSIZE
  #ifdef __LP64__
    #define __WORDSIZE 64
  #else /* ! __LP64__ */
    #define __WORDSIZE 32
  #endif
#endif
/* end patch for issue #1 */

#if __WORDSIZE != 64
  #error "只支持 64 位。"
#endif

#define PN_TNIL         0x250000
#define PN_TNUMBER      (1+PN_TNIL)
#define PN_TBOOLEAN     (2+PN_TNIL)
// TODO hsq 这些类型 ID 与上面的 struct 及其他处定义的对应关系
#define PN_TSTRING      (3+PN_TNIL)
#define PN_TWEAK        (4+PN_TNIL)
#define PN_TCLOSURE     (5+PN_TNIL)
#define PN_TTUPLE       (6+PN_TNIL)
#define PN_TSTATE       (7+PN_TNIL)
#define PN_TFILE        (8+PN_TNIL)
#define PN_TOBJECT      (9+PN_TNIL)
#define PN_TVTABLE      (10+PN_TNIL)
#define PN_TSOURCE      (11+PN_TNIL)
#define PN_TBYTES       (12+PN_TNIL)  // PN_TBYTES 有缓冲作用
#define PN_TPROTO       (13+PN_TNIL)
#define PN_TLOBBY       (14+PN_TNIL)
#define PN_TTABLE       (15+PN_TNIL)
#define PN_TLICK        (16+PN_TNIL)
#define PN_TFLEX        (17+PN_TNIL)
#define PN_TSTRINGS     (18+PN_TNIL)  // VM 的字符串表
#define PN_TERROR       (19+PN_TNIL)
#define PN_TCONT        (20+PN_TNIL)
#define PN_TUSER        (21+PN_TNIL)
#define PN_TBUILTIN_END PN_TUSER      // 内置类型结果，后面是用户定义类
#define PN_TBUILTIN_NUM (PN_TYPE_ID(PN_TBUILTIN_END) + 1)

// 声明 t 类型结构体的易变指针
#define vPN(t)          struct PN##t * volatile
// 取 PN_Txxx: 全类型
#define PN_TYPE(x)      potion_type((PN)(x))
// 取 PN_Txxx: 对象(指针）类型
#define PN_VTYPE(x)     (((struct PNObject *)(x))->vt)
// 类型数值 t 对应的索引，用于索引类型数组中的虚表 .vts
#define PN_TYPE_ID(t)   ((t)-PN_TNIL)
// 取类型数值 t 的虚表位置（指针）
#define PN_VTABLE(t)    (PN_FLEX_AT(P->vts, PN_TYPE_ID(t)))
// 检查类型数值 t 是否有效（有虚表）(当前调用 t 总是无符号，所以 PN_TYPE_ID(t)>=0 一直成立)
// #define PN_TYPECHECK(t) (PN_TYPE_ID(t) >= 0 && PN_TYPE_ID(t) < PN_FLEX_SIZE(P->vts))
#define PN_TYPECHECK(t) (PN_TYPE_ID(t) < PN_FLEX_SIZE(P->vts))

// 原生类型: nil bool number(int)
// TODO hsq 函数调用时，给形参类型为 PN 传入实参 0 的，都用 PN_NIL 代替？
#define PN_NIL          ((PN)0)
#define PN_ZERO         ((PN)1)
#define PN_FALSE        ((PN)2)
#define PN_TRUE         ((PN)6)
// 未使用 #define PN_PRIMITIVE    7
// 对象（指针）类型的掩码
#define PN_REF_MASK     ~7
// TODO hsq none 如何与 number 区分？
#define PN_NONE         ((PN_SIZE)-1)
#define POTION_FWD      0xFFFFFFFE
#define POTION_COPIED   0xFFFFFFFF

// 数字类型标志位
#define PN_FNUMBER      1
// 布尔类型标志位
#define PN_FBOOLEAN     2
// 布尔测试：是否为真(!nil && !false)
#define PN_TEST(v)       ((PN)(v) != PN_FALSE && (PN)(v) != PN_NIL)
// 各类型值转为 bool 值
// #define PN_BOOL(v)       (PN_TEST(v) ? PN_TRUE : PN_FALSE)
// 真值(0 或 1)转为 bool 值
// #define PN_BOOL(v)       ((v) ? PN_TRUE : PN_FALSE)
#define PN_BOOL(v)       (((v) << 2) + PN_FALSE)
// 是否指针（对象类型）
#define PN_IS_PTR(v)     (!PN_IS_NUM(v) && ((PN)(v) & PN_REF_MASK))
// 是否 nil
#define PN_IS_NIL(v)     ((PN)(v) == PN_NIL)
// 是否 bool 类型 ；应该先排除 number 类型，当前只一处调用，而且排除了。
#define PN_IS_BOOL(v)    ((PN)(v) & PN_FBOOLEAN)
// 是否 number 类型（整数）
#define PN_IS_NUM(v)     ((PN)(v) & PN_FNUMBER)
// 是否 tuple 类型
#define PN_IS_TUPLE(v)   (PN_TYPE(v) == PN_TTUPLE)
// 是否 string 类型
#define PN_IS_STR(v)     (PN_TYPE(v) == PN_TSTRING)
// 是否 table 类型
#define PN_IS_TABLE(v)   (PN_TYPE(v) == PN_TTABLE)
// 是否 closure 类型
#define PN_IS_CLOSURE(v) (PN_TYPE(v) == PN_TCLOSURE)
// 是否 decimal(小数) 类型
#define PN_IS_DECIMAL(v) (PN_IS_PTR(v) && PN_TYPE(v) == PN_TNUMBER)
// 是否 proto 类型
#define PN_IS_PROTO(v)   (PN_TYPE(v) == PN_TPROTO)
// 是否 ref(weak) 类型
#define PN_IS_REF(v)     (PN_TYPE(v) == PN_TWEAK)
// 是否 Error 类型
#define PN_IS_ERROR(v)   (PN_TYPE(v) == PN_TERROR)

// 封装为 number （比 long 少一位）
// TODO hsq 是否会造成比较大的数字变小？
#define PN_NUM(i)       ((PN)((((long)(i))<<1) + PN_FNUMBER))
// 从 number 中取出整数（有符号）
#define PN_INT(x)       (((long)(x))>>1)
// 未使用 #define PN_PREC 16
// 产生随机数(number)
// 未使用 #define PN_RAND()       PN_NUM(potion_rand_int())
#define PN_LITERAL(TEXT)  potion_str2(P, TEXT, sizeof TEXT - 1)
// 用 c 字符串构造 PNString 。
#define PN_STRING(str)    potion_str2(P, (str), strlen(str))
// 取 String/bytes 的 char * 指针
#define PN_STR_PTR(x)   potion_str_ptr(x)
// 取 String/bytes 的 .len 字段
#define PN_STR_LEN(x)   ((struct PNString *)(x))->len
// 获取 PNBytes 在字符串池中的唯一副本指针，按需加入。
#define PN_STR_B(x)     potion_bytes_string(P, PN_NIL, x)
// 将 x 强制转为闭包指针。
#define PN_CLOSURE(x)   ((struct PNClosure *)(x))
// 取闭包 x 的方法字段。
#define PN_CLOSURE_F(x) ((struct PNClosure *)(x))->method
// 将 x 强制转为原型指针。
#define PN_PROTO(x)     ((struct PNProto *)(x))
// 创建闭包，无额外数据， f 为函数， s 为生成签名用的代码串。
#define PN_FUNC(f, s)   potion_closure_new(P, (PN_F)f, potion_sig(P, s), 0)
// 解除弱引用，即其 .data 字段。
#define PN_DEREF(x)     ((struct PNWeakRef *)(x))->data
#define PN_TOUCH(x)     potion_gc_update(P, (PN)(x))

// 返回不小于 o 的 x 的最小整倍数。
#define PN_ALIGN(o, x)   (((((o) - 1) / (x)) + 1) * (x))
// 声明名为 N 的 GC 对象结构，带类型 T 的柔性数组。
#define PN_FLEX(N, T)    typedef struct { PN_OBJECT_HEADER PN_SIZE len; PN_SIZE siz; T ptr[0]; } N
// 取 N 指向的 PN_FLEX 创建的类型的索引 I 处的 PN
#define PN_FLEX_AT(N, I) ((PNFlex *)(N))->ptr[I]
// 取 N 指向的 N_FLEX 创建的类型的 PN 列表长度字段 .len
#define PN_FLEX_SIZE(N)  ((PNFlex *)(N))->len

#if PN_SIZE_T == 4
  #define PN_NUMHASH(x)   x
#else
  #define PN_NUMHASH(x)   (PNUniq)((x)>>33^(x)^(x)<<11)
#endif
// 取指针类型的 uniq 字段，或原生类型即时计算哈希值
#define PN_UNIQ(x)      (PN_IS_PTR(x) ? ((struct PNObject *)(x))->uniq : PN_NUMHASH(x))

// 未使用 #define PN_IS_EMPTY(T)  (PN_GET_TUPLE(T)->len == 0)
// 创建 0 项的空元组。
#define PN_TUP0()       potion_tuple_empty(P)
// 为 X 创建 1 项的元组。
#define PN_TUP(X)       potion_tuple_new(P, X)
// 将 X 附加到 PNTuple *T 末尾，返回 tuple ；需重新分配多一项的空间。
#define PN_PUSH(T, X)   potion_tuple_push(P, T, X)
// 在元组中查找值为 X 的项的索引；无则为 -1(PN_NONE) 。
#define PN_GET(T, X)    potion_tuple_find(P, T, X)
// 在元组中查找值为 X 的项的索引；无则加入。
#define PN_PUT(T, X)    potion_tuple_push_unless(P, T, X)
// 跳过各级 PNFwd
#define PN_GET_TUPLE(t) ((struct PNTuple *)potion_fwd(t))
// 获取元组的项数
#define PN_TUPLE_LEN(t) PN_GET_TUPLE(t)->len
// 获取元组索引 n 处的项
#define PN_TUPLE_AT(t, n) PN_GET_TUPLE(t)->set[n]
// 遍历元组，对每项执行代码 B ，I 是 B 中索引的变量名，但 I 不是 PN ；
//    不要在 B 中直接使用 T 的值（因为可能是 PNFwd 间接引用）。
#define PN_TUPLE_COUNT(T, I, B) ({ \
    struct PNTuple * volatile __t##I = PN_GET_TUPLE(T); \
    if (__t##I->len != 0) { \
      PN_SIZE I; \
      for (I = 0; I < __t##I->len; I++) B \
    } \
  })
// 遍历元组，对每项执行代码 B ，I 和 V 是 B 中索引和值的变量名，但 I 不是 PN ；
//    不要在 B 中直接使用 T 的值（因为可能是 PNFwd 间接引用）。
#define PN_TUPLE_EACH(T, I, V, B) ({ \
    struct PNTuple * volatile __t##V = PN_GET_TUPLE(T); \
    if (__t##V->len != 0) { \
      PN_SIZE I; \
      for (I = 0; I < __t##V->len; I++) { \
        PN V = __t##V->set[I]; \
        B \
      } \
    } \
  })

// 可 GC 对象头部字段。
#define PN_OBJECT_HEADER \
  PNType vt; \
  PNUniq uniq;

//
// standard objects act like C structs
// the fields are defined by the type
// and it's a fixed size, not volatile.
//
struct PNObject {
  PN_OBJECT_HEADER
  PN ivars[0];
};

//
// forwarding pointer (in case of
// reallocation)
//
struct PNFwd {
  unsigned int fwd;
  PN_SIZE siz;
  PN ptr;
};

//
// struct to wrap arbitrary data that
// we may want to allocate from Potion.
//
struct PNData {
  PN_OBJECT_HEADER
  PN_SIZE siz;
  char data[0];
};

// 不可变 UTF-8 字符串，以 '\0' 结尾， .len 不含 '\0' 。
// strings are immutable UTF-8, the ID is
// incremental and they may be garbage
// collected. (non-volatile)
//
struct PNString {
  PN_OBJECT_HEADER
  PN_SIZE len;
  char chars[0];
};

//
// byte strings are raw character data,
// volatile, may be appended/changed.
//
struct PNBytes {
  PN_OBJECT_HEADER;
  PN_SIZE len;
  PN_SIZE siz;
  char chars[0];
};

// 未使用 #define PN_MANTISSA(d, n) d->real[1 + n]

//
// decimals are floating point numbers
// stored as binary data. immutable.
//
struct PNDecimal {
  PN_OBJECT_HEADER
  double value;
};

//
// a file is wrapper around a file
// descriptor, non-volatile but mutable.
//
struct PNFile {
  PN_OBJECT_HEADER
  int fd;
  PN path;
  PN mode;
};

#define ERROR(format, ...) \
  potion_error(P, potion_str_format(P, (format), ##__VA_ARGS__), 0, 0, PN_NIL)

#define PN_MSG(name, ARGS...) static PN name(Potion *P, PN closure, PN self, ##ARGS)
#define PN_MSG_G(name, ARGS...) PN name(Potion *P, PN closure, PN self, ##ARGS)
// #define PN_MSG(name, ...) static PN name(Potion *P, PN closure, PN self, ##__VA_ARGS__)
// #define PN_MSG_G(name, ...) PN name(Potion *P, PN closure, PN self, ##__VA_ARGS__)

// 类型签名：(Potion *P, PN closure, PN self, ...) -> PN
typedef PN (*PN_F)(Potion *, PN, PN, ...);

//
// a closure is an anonymous function,
// non-volatile.
//
struct PNClosure {
  PN_OBJECT_HEADER
  PN_F method;
  PN sig;         // 若.data 非空则为PNProto 的参数签名；否则是发消息时的额外参数
  PN_SIZE extra;  // .data 项数
  PN data[0];     // 若非空则首个是 PNProto ，后续是可选的用 PNWeakRef 包装的 upval
};

//
// An AST fragment, non-volatile.
//
struct PNSource {
  PN_OBJECT_HEADER
  unsigned char type;   // AST 节点分类索引，用于查找名字和 .a 项数
  PN a[0];
};

//
// a prototype is compiled source code,
// non-volatile.
//
struct PNProto {
  PN_OBJECT_HEADER
  PN source; // program name or enclosing scope 文件名|外层 PNProto
  PN sig;    // argument signature              元组
  // TODO hsq .paths 为什么不直接存储字符串？
  PN paths;  // paths (instance variables)      元组(.values 中的索引)
  PN locals; // local variables                 元组，索引也是参数在寄存器（栈中）的索引。
  PN upvals; // variables in upper scopes       元组
  PN values; // numbers, strings, etc.          元组
  PN protos; // nested closures                 元组(PNProto)
  PN tree;   // abstract syntax tree            PNSource
  PN_SIZE stacksize;
  PN ops;    // assembled instructions          PNAsm(.vt=PN_TBYTES)
  PN_F jit;  // jit function pointer            先 PN_SIZE len 后 PN_F jit
  PN_FLAGS flags;
};
#define LOCALSIZE(f) PN_TUPLE_LEN((f)->locals)
#define UPVALSIZE(f) PN_TUPLE_LEN((f)->upvals)

#define HAS_UPVALS(f)          ((f)->flags & 0x01) // 子函数是否有 upvals （当前实现中 upvals 向上传递）
#define HAS_UPVALS_SET(f, yes) ((f)->flags |= ((yes) ? 0x01 : 0))

#define IS_LOADED(f)          ((f)->flags & 0x02) // 预编译加载，而非即时编译
#define IS_LOADED_SET(f, yes) ((f)->flags |= ((yes) ? 0x02 : 0))

#define JIT_LEN_SIZE  (sizeof(PN_SIZE))
#define JIT_LEN(buf)  (*(PN_SIZE *)(buf))
#define JIT_CODE(buf) ((PN_F)((PN_SIZE *)(buf) + 1))

//
// a tuple is an ordered list,
// volatile.
//
struct PNTuple {
  PN_OBJECT_HEADER
  unsigned long len;
  PN set[0];
};

//
// a weak ref is used for upvals, it acts as
// a memory slot, non-volatile but mutable.
//
struct PNWeakRef {
  PN_OBJECT_HEADER
  PN data;
};

//
// an error, including a description,
// file location, a brief excerpt.
//
struct PNError {
  PN_OBJECT_HEADER
  // 未使用 PN parent;
  PN message;    // string/bytes   描述
  int line, chr; // number   位置（行号、字符编号）
  PN excerpt;    // string/bytes   摘录、节选
};

//
// a lick is a unit of generic tree data.
//
struct PNLick {
  PN_OBJECT_HEADER
  PN name;      // 类型： PNString
  PN attr;      // 类型： PNTable | PN_NIL
  PN inner;     // 类型： PNTuple | PNString | PN_NIL
};

//
// a continuation saves the stack and all
// stack pointers.
//
struct PNCont {
  PN_OBJECT_HEADER
  PN_SIZE len;
  PN stack[0]; // [0] = head of potion stack
               // [1] = current %rsp
               // [2] = current %rbp
               // [3+] = full stack dump, ascending
};

// 获取 PN obj 类型：引用类型先跳过 PNFwd 再取最终的 .vt 字段。
// the potion type is the 't' in the vtable tuple (m,t)
static inline PNType potion_type(PN obj) {
  // TODO hsq 判断分支太多，可优化？如推导类型生成类型特定的代码；加入 likely ？
  if (PN_IS_NUM(obj))  return PN_TNUMBER;
  if (PN_IS_BOOL(obj)) return PN_TBOOLEAN;
  if (PN_IS_NIL(obj))  return PN_TNIL;
  while (1) {
    struct PNFwd *o = (struct PNFwd *)obj;
    if (o->fwd != POTION_FWD)
      return ((struct PNObject *)o)->vt;
    obj = o->ptr;
  }
}

// 在一次类型为 t 的对象 obj 重分配存储后，执行一次 forward 并更新该变量；
//    常用于 kh_put 之后。
// macro for doing a single fwd check after a possible realloc
#define PN_QUICK_FWD(t, obj) \
  if (((struct PNFwd *)obj)->fwd == POTION_FWD) \
    obj = (t)(((struct PNFwd *)obj)->ptr);

// 跳过对象 obj 的所有 forward 引用，返回指向的原始对象。
// resolve forwarding pointers for mutable types (PNTuple, PNBytes, etc.)
static inline PN potion_fwd(PN obj) {
  // TODO hsq 优化？以及其他 POTION_FWD 相关处的类似优化？
  while (PN_IS_PTR(obj) && ((struct PNFwd *)obj)->fwd == POTION_FWD)
    obj = ((struct PNFwd *)obj)->ptr;
  return obj;
}

// quick access to either PNString or PNByte pointer
static inline char *potion_str_ptr(PN s) {
  // TODO hsq 需要确保 s 指向 String 结构
  if (((struct PNString *)s)->vt == PN_TSTRING)
    return ((struct PNString *)s)->chars;
  s = potion_fwd(s);
  return ((struct PNBytes *)s)->chars;
}

PN_FLEX(PNFlex, PN); // PN 的 Flex 。
PN_FLEX(PNAsm, unsigned char); // uchar 的 FLex 。

// TODO hsq 这些相关的声明没必要在这里，移到其他内部头文件中？
// the jit
//
#define OP_MAX 64
// 类型签名：(Potion *P, PNProto *, PNAsm * *, ...) -> void
typedef void (*OP_F)(Potion *P, struct PNProto *, PNAsm * volatile *, ...);

#define TARGET_PROTO_SIG Potion *P, struct PNProto * volatile f, PNAsm * volatile *asmp
#define TARGET_PROTO_ARGS P, f, &asmb
#define TARGET_OP_SIG TARGET_PROTO_SIG, PN_SIZE pos
#define TARGET_OP_ARGS TARGET_PROTO_ARGS, pos

typedef struct {
  void (*setup)    (TARGET_PROTO_SIG, long);
  void (*stack)    (TARGET_PROTO_SIG, long);
  void (*registers)(TARGET_PROTO_SIG, long);
  void (*local)    (TARGET_PROTO_SIG, long, long);
  void (*upvals)   (TARGET_PROTO_SIG, long, long, int);
  void (*jmpedit)  (TARGET_PROTO_SIG, unsigned char *, int);
  OP_F op[OP_MAX];
  void (*finish)   (TARGET_PROTO_SIG);
  void (*mcache)   (Potion *, struct PNVtable * volatile, PNAsm * volatile *);
  void (*ivars)    (Potion *, PN, PNAsm * volatile *);
} PNTarget;

//
// the interpreter
// (one per thread, houses its own garbage collector)
//
struct Potion_State {
  PN_OBJECT_HEADER
  PNTarget target;
  struct PNTable *strings; /* table of all strings */
  PN lobby;                /* root namespace 类型：虚表 */
  PNFlex * volatile vts;   /* built in types */

  // parser: 解析源码、函数签名
  PN input;              /* parser input 类型为 PNBytes */
  PN source;             /* parser output 类型为 PNTuple */
  PN_SIZE yypos;         /* parser buffer position */
  PNAsm * volatile pbuf; /* parser buffer 类型为 PN */
  // 未使用 PN unclosed;  /* used by parser for named block endings */

  PN call, callset;     /* generic call and callset */
  // 未使用 int prec;    /* decimal precision */
  struct PNMemory *mem; /* allocator/gc */

  // struct PNTable * modules; // 已加载的模块表
  // TODO hsq 当前参考 PNProto.methods ， Potion.strings 的 key 是 const char* 更好？
  vPN(Table) modules; // 已加载的模块表
  PN_FLAGS jit_has:1; // 是否支持 JIT
  PN_FLAGS jit_on:1;  // 是否开启 JIT
  PN_FLAGS in_repl:1; // 是否 REPL
  // TODO hsq 把 flags 映射为一个对象？
};

//
// the garbage collector
//
// TODO hsq PNMemory 等结构定义放在其他内部头文件中更好？
struct PNMemory {
  // the birth region
  volatile void *birth_lo, *birth_hi, *birth_cur;
  volatile void **birth_storeptr;

  // the old region (TODO: consider making the old region common to all threads)
  volatile void *old_lo, *old_hi, *old_cur;

  volatile int collecting, dirty, pass, majors, minors;
  void *cstack;  /* machine stack start */
  void *protect; /* end of protected memory */
};

// 创建系统栈变量 x 记录前一变量的地址（内容为 0x571FF ）。
#define POTION_INIT_STACK(x) \
  PN __##x = 0x571FF; void *x = (void *)&__##x

void potion_garbagecollect(Potion *, int, int);
PN_SIZE potion_type_size(Potion *, const struct PNObject *);
unsigned long potion_rand_int();
double potion_rand_double();

// quick inline allocation
static inline void *potion_gc_alloc(Potion *P, PNType vt, PN_SIZE siz) {
  struct PNMemory *M = P->mem;
  struct PNObject *res = 0;
  if (siz < sizeof(struct PNFwd))
    siz = sizeof(struct PNFwd);
  siz = PN_ALIGN(siz, 8); // force 64-bit alignment
  // TODO hsq dirty 还没 =1 的情况。
  if (M->dirty || (char *)M->birth_cur + siz >= (char *)M->birth_storeptr - 2)
    potion_garbagecollect(P, siz + 4 * sizeof(double), 0);
  res = (struct PNObject *)M->birth_cur;
  res->vt = vt;
  res->uniq = (PNUniq)potion_rand_int();
  M->birth_cur = (char *)res + siz;
  return (void *)res;
}

// TODO: mmap already inits to zero?
static inline void *potion_gc_calloc(Potion *P, PNType vt, PN_SIZE siz) {
  return potion_gc_alloc(P, vt, siz);
}

static inline void potion_gc_update(Potion *P, PN x) {
  struct PNMemory *M = P->mem;
  if ((x > (PN)M->birth_lo && x < (PN)M->birth_hi && (x < (PN)M || x >= (PN)M->protect)) ||
      x == (PN)M->birth_storeptr[1] ||
      x == (PN)M->birth_storeptr[2] ||
      x == (PN)M->birth_storeptr[3])
       return;
  *(M->birth_storeptr--) = (void *)x;
  if ((void **)M->birth_storeptr - 4 <= (void **)M->birth_cur)
    potion_garbagecollect(P, POTION_PAGESIZE, 0);
}

static inline void *potion_gc_realloc(Potion *P, PNType vt, struct PNObject * volatile obj, PN_SIZE sz) {
  void *dst = 0;
  PN_SIZE oldsz = 0;

  if (obj != NULL) {
    oldsz = potion_type_size(P, (const struct PNObject *)obj);
    if (oldsz >= sz)
      return (void *)obj;
  }

  dst = potion_gc_alloc(P, vt, sz);
  if (obj != NULL) {
    memcpy(dst, (void *)obj, oldsz);
    ((struct PNFwd *)obj)->fwd = POTION_FWD;
    ((struct PNFwd *)obj)->siz = oldsz;
    ((struct PNFwd *)obj)->ptr = (PN)dst;
    potion_gc_update(P, (PN)obj);
  }

  return dst;
}

// 创建 PNData ， .vt=PN_TUSER 。
static inline struct PNData *potion_data_alloc(Potion *P, PN_SIZE siz) {
  struct PNData *data = (struct PNData *)potion_gc_alloc(P, PN_TUSER, sizeof(struct PNData) + siz);
  data->siz = siz;
  return data;
}

// 用于 potion_gc_minor 和 potion_gc_major
// internal errors
//
#define POTION_OK       0
#define POTION_NO_MEM   8910

// 向 RCV 发 MSG 消息，并返回与消息绑定的值；如该值是闭包则调用之。
// method caches
// (more great stuff from ian piumarta)
//
#define potion_send(RCV, MSG, ARGS...) ({ \
    PN r = (PN)(RCV); \
    PN c = potion_bind(P, r, (MSG)); \
    if (PN_IS_CLOSURE(c)) \
      c = ((struct PNClosure *)c)->method(P, c, r, ##ARGS); \
    c; \
  })

// 注册消息响应对象， MSG 为字符串字面量。
#define PN_DEF(RCV, MSG, OBJ) \
  potion_send(RCV, $(def), PN_LITERAL(MSG), (OBJ))
// 注册消息响应闭包， MSG 为字符串字面量。
#define PN_METHOD(RCV, MSG, FN, SIG) \
  potion_send(RCV, $(def), PN_LITERAL(MSG), PN_FUNC(FN, SIG))

#define $(name) PN_##name

extern PN /* $(allocate), */ $(break), $(call), $(class), $(compile),
  $(continue), $(def), $(delegated), $(else), $(elsif), $(elseif), $(if),
  $(lookup), $(loop), $(print), $(return), $(self), $(string),
  $(while);
extern PN $(add), $(sub), $(mult), $(div), $(rem),
  $(bitn), $(bitl), $(bitr), $(pipe), $(caret), $(amp);

//
// the Potion functions
//
// TODO hsq 检查函数可见性：
//    内部模块使用，不导出？ internal
//    定义为消息的，不导出？
//    在 A 定义，只在 B 调用，调整定义或调用位置？如 数值计算系列等
//    static (以及未在这里声明的函数)


// fmt 必需是字符串字面量
#define FAIL_MSG(fmt,...) fprintf(stderr, "** " fmt, ##__VA_ARGS__)
#define STR_AND_PRINT(x) potion_send(potion_send((x), $(string)), $(print))
#define POTION_BYTES_LITERAL(bytes, text) potion_bytes_literal(P, (bytes), (text), sizeof(text) - 1)

PN read_file(Potion *P, const char *filename);
PN load_or_compile(Potion *P, const char *filename);

Potion *potion_create(void *sp);
void potion_destroy(Potion *P);
PN potion_error(Potion *P, PN msg, long lineno, long charno, PN excerpt);
// 未使用 PNType potion_kind_of(PN obj);
// void potion_p(Potion *P, PN x);
PN potion_str2(Potion *P, const char *str, size_t len);
PN potion_str_format(Potion *P, const char *format, ...);
PN potion_bytes_str(Potion *P, const char *str);
PN potion_bytes(Potion *P, size_t len);
PN_MSG_G(potion_bytes_string);
PN_SIZE potion_bytes_format(Potion *P, PN bytes, const char *format, ...);
PN_SIZE potion_bytes_literal(Potion *P, PN bytes, const char *text, PN_SIZE len);
void potion_bytes_obj_string(Potion *P, PN bytes, PN obj);
PN_MSG_G(potion_def_method, PN key, PN method);
PN potion_type_new(Potion *P, PNType t, PN self);
void potion_type_call_is(PN vt, PN cl);
void potion_type_callset_is(PN vt, PN cl);
PN potion_class(Potion *P, PN closure, PN parent, PN ivars);
PN potion_obj_get_call(Potion *P, PN obj);
PN potion_obj_get_callset(Potion *P, PN obj);
PN potion_obj_get(Potion *P, PN obj, PN ivar);
PN potion_obj_set(Potion *P, PN obj, PN ivar, PN value);
PN_MSG_G(potion_object_new);
PN potion_call(Potion *P, PN cl, PN_SIZE argc, PN * volatile argv);
PN_MSG_G(potion_lookup, PN key);
PN potion_bind(Potion *P, PN rcv, PN msg);
PN potion_message(Potion *P, PN rcv, PN msg);
PN potion_closure_new(Potion *P, PN_F meth, PN sig, PN_SIZE extra);
PN potion_ref(Potion *P, PN data);
// 解析字符串 fmt 生成参数签名元组；见 syntax.g 定义处。
PN potion_sig(Potion *P, char *fmt);
// 在闭包 cl 的签名元组 .sig 中查找 name ，返回参数编号，无则为 -1 ；见 syntax.g 定义处。
int potion_sig_find(Potion *P, PN cl, PN name);
PN potion_decimal(Potion *P, char *str, int len);
PN_MSG_G(potion_pow, PN sup);

PN potion_obj_add(Potion   *P, PN a, PN b);
PN potion_obj_sub(Potion   *P, PN a, PN b);
PN potion_obj_mult(Potion  *P, PN a, PN b);
PN potion_obj_div(Potion   *P, PN a, PN b);
PN potion_obj_rem(Potion   *P, PN a, PN b);
PN potion_obj_bitn(Potion  *P, PN a);
PN potion_obj_bitl(Potion  *P, PN a, PN b);
PN potion_obj_bitr(Potion  *P, PN a, PN b);
PN potion_obj_pipe(Potion  *P, PN a, PN b);
PN potion_obj_caret(Potion *P, PN a, PN b);
PN potion_obj_amp(Potion   *P, PN a, PN b);

PN potion_tuple_empty(Potion *P);
PN potion_tuple_with_size(Potion *P, unsigned long size);
PN potion_tuple_new(Potion *P, PN value);
PN potion_tuple_push(Potion *P, PN tuple, PN value);
PN_SIZE potion_tuple_push_unless(Potion *P, PN tuple, PN value);
PN_SIZE potion_tuple_find(Potion *P, PN tuple, PN value);
PN potion_table_empty(Potion *P);
PN_MSG_G(potion_table_put, PN key, PN value);
PN potion_table_set(Potion *P, PN self, PN key, PN value);
// 暂未使用 long potion_tuple_binary_search(PN self, PN x);
// 未使用 void potion_tuple_ins_sort(PN self);
PN potion_lick(Potion *P, PN name, PN inner, PN attr);
PN potion_source_load(Potion *P, PN buf);
PN potion_source_dump(Potion *P, PN proto);

Potion *potion_gc_boot(void *sp);
void potion_lobby_init      (Potion *P);
void potion_rand_init       (Potion *P);
void potion_object_init     (Potion *P);
void potion_primitive_init  (Potion *P);
void potion_num_init        (Potion *P);
void potion_str_hash_init   (Potion *P);
void potion_mod_hash_init   (Potion *P);
void potion_str_init        (Potion *P);
void potion_table_init      (Potion *P);
void potion_source_init     (Potion *P);
void potion_lick_init       (Potion *P);
void potion_compiler_init   (Potion *P);
void potion_vm_init         (Potion *P);
void potion_file_init       (Potion *P);
void potion_module_init     (Potion *P);
void potion_cont_init       (Potion *P);
// #ifndef DYLIB
// PN   potion_lineedit_init   (Potion *P);
// #endif
// 未使用 void potion_dump_stack(Potion *P);

PN_MSG_G(potion_num_string);
int potion_gc_reserved(Potion *P);
int potion_gc_actual(Potion *P);
int potion_gc_fixed(Potion *P);

// 解析源码 PNBytes code -> PNSource | error ；见 syntax.g 定义处。
PN potion_parse(Potion *P, PN code);
PN potion_vm_class(Potion *P, PN closure, PN parent);
PN potion_vm(Potion *P, PN proto, PN self, PN vargs, PN_SIZE upc, PN * volatile upargs);
PN potion_eval(Potion *P, PN bytes);
PN potion_run(Potion *P, PN code);
#if POTION_JIT == 1
  PN_F potion_jit_proto(Potion *P, PN proto);
  PN_MSG_G(potion_jit_on, PN on);
#endif

#endif
